/* Viper Split# Compiler
 * Copyright (c) 2013 Split# Developers
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 */


/* NOTE: The above license DOES NOT APPLY to the Viper and GruntXProductions
 * namespace. Viper is released under the GNU General Public License v3
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using GruntXProductions;

namespace SplitSharp
{
    public class Compiler
    {

        private static bool Verbose = false;
        private static bool EmitAssembly = false;
        private static bool doCompile = true;
        private static bool interpret = false;
        private static bool Werror = false;
        public static CodeGen cgen;
        public static Lexer lexer;
        public static Parser parser;
        private static void outputDataRecieved(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
                Console.WriteLine(e.Data.ToString());
        }
        private static void errorDataRecieved(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
                Console.Error.WriteLine(e.Data.ToString());
        }
        /* Split# files are compiled in 3 stages
         * Stage 1: Lexical analysis. A token list is generated.
         * Stage 2: Semantic anaylsis. A syntax tree is genereted.
         * Stage 3: Code generation. Viper IL is created
         */
        public static void Main(string[] args)
        {
            try
            {
                cgen = new CodeGen();
                if (args.Length == 0)
                {
                    Console.Error.WriteLine("Fatal error: No input!");
                    return;
                }
                else if (args[0] == "--help" || args[0] == "-h" || args[0] == "-help" || args[0] == "?")
                {

                }
                string input = args[0];
                string output = Path.GetFileNameWithoutExtension(input) + ".gex";
                List<string> libs = new List<string>();
                for (int i = 1; i < args.Length; i++)
                {
                    if (args[i] == "-verbose" || args[i] == "--verbose")
                    {
                        Verbose = true;
                    }
                    else if (args[i] == "-s" || args[i] == "--s")
                    {
                        output = Path.GetFileNameWithoutExtension(input) + ".il";
                        EmitAssembly = true;
                    }
                    else if (args[i].ToLower() == "-werror" || args[i].ToLower() == "--werror")
                        Werror = true;
                    else if (args[i] == "-i" || args[i] == "--jit" || args[i] == "-jit")
                        interpret = true;
                    else if (args[i] == "-uselib" || args[i] == "--uselib")
                    {
                        libs.Add(args[i + 1]);
                        ++i;
                    }
                    else if (args[i] == "-o" || args[i] == "--o")
                    {
                        ++i;
                        output = args[i];
                    }

                }
                if (Verbose)
                    Console.WriteLine("Scanning input...");
                lexer = new Lexer();
                StringReader sr = new StringReader(File.ReadAllText(input));
                try
                {
                    lexer.Scan(sr);
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine("Fatal error during scanning: " + e.Message);
                    return;
                }
                parser = new Parser(lexer.tokens);
                if (Verbose)
                    Console.WriteLine("Parsing Input...");
                try
                {
                    parser.BeginParse();
                }
                catch (Exception e)
                {
                    cgen.Errors.Add("Fatal error during parsing: " + e.Message);
                }
                if (Verbose)
                    Console.WriteLine("Generating Viper IL Code...");
                cgen.Assembler = new Viper.Assembly();
                cgen.Compile(parser.AST);
                if (cgen.Errors.Count > 0 || (Werror && cgen.Warnings.Count > 0))
                    doCompile = false;
                if (doCompile)
                {
                    if (EmitAssembly)
                    {
                        // File.WriteAllText(output, cgen.IL);

                    }
                    else
                    {
                        /*
                         * Statically link any referenced libraries...
                         */
                        foreach (string lib in libs)
                            cgen.Assembler.Merge(Viper.GEX.Open(lib));
                        if (interpret)
                        {
                            /* Basically right here instead of actually 
                             * interpreting it, we will create a tempory 
                             * file and then pass it to Viper.
                             */
                            string randPath = Path.GetTempPath() + "tmp" + getRandomString() + ".gex";
                            cgen.Assembler.SaveGEX(randPath);
                            Process p = new Process();
                            p.StartInfo.FileName = "viper";
                            p.StartInfo.RedirectStandardError = true;
                            p.StartInfo.RedirectStandardInput = true;
                            p.StartInfo.RedirectStandardOutput = true;
                            p.StartInfo.UseShellExecute = false;
                            p.OutputDataReceived += outputDataRecieved;
                            p.ErrorDataReceived += errorDataRecieved;
                            p.StartInfo.WorkingDirectory = System.Environment.CurrentDirectory;
                            p.StartInfo.Arguments = randPath + " --gex";
                            p.Start();
                            p.BeginErrorReadLine();
                            p.BeginOutputReadLine();
                            p.WaitForExit();
                            File.Delete(randPath);
                            return;
                        }
                        else
                        {
                            cgen.Assembler.Save(output);
                        }
                    }
                }
                else
                {
                    Console.Error.WriteLine("Compilation failed with " + cgen.Errors.Count.ToString() + " error(s)!");
                    foreach (string str in cgen.Errors)
                        Console.Error.WriteLine("Error: " + str);
                }

            }
            catch (Exception e)
            {
                Console.Error.WriteLine("Fatal error! Compilation halted!");
                Console.Error.WriteLine(e.ToString());
            }
        }
        private static string getRandomString()
        {
            Random r = new Random();
            StringBuilder randStr = new StringBuilder("");
            for (int i = 0; i < 25; i++)
            {
                randStr.Append(((char)r.Next(65, 90)).ToString());
            }
            return randStr.ToString();
        }

    }
}
